JavaScript高级知识汇总(高级篇)

您所在的位置:网站首页 js 高级技巧 JavaScript高级知识汇总(高级篇)

JavaScript高级知识汇总(高级篇)

2023-07-26 04:41| 来源: 网络整理| 查看: 265

目录 JavaScript高级知识总结(高级篇)一、深入基础1.1数据类型1.2数据变量与内存1.3对象1.4函数回调函数 1.5 IIFE1.6函数中的this 二、函数高级2.1原型与原型链2.1.1原型(prototype)2.1.2显示原型与隐式原型2.1.3原型链2.1.4原型链属性问题2.1.5探索instanceof2.1.6原型面试题 2.2执行上下文与执行上下文栈2.2.1变量提升与函数提升2.2.2执行上下文2.2.3执行上下文栈2.2.4执行上下文22.2.5面试题 2.3作用域与作用域链2.3.1作用域2.3.2作用域与执行上下文2.3.3作用域链2.3.4作用域面试题 2.4闭包2.4.1理解闭包2.4.2常见的闭包2.4.3闭包的作用2.4.4闭包的生命周期2.4.5闭包的应用_自定义JS模块2.4.6闭包的缺点及解决2.4.7内存溢出与内存泄漏2.4.8闭包面试题 三、对象高级3.1对象的创建模式3.1.1Object构造函数模式3.1.2对象字面量模式3.1.3工厂模式3.1.4自定义构造函数模式3.1.5构造函数+原型组合模式 3.2继承模式3.2.1原型链继承3.2.2借用构造函数继承3.2.3组合继承 四、线程机制与事件机制4.1进程与线程4.2定时器引发的思考4.3JS是单线程的4.4事件循环模型4.5Web Workers

JavaScript高级知识总结(高级篇) 一、深入基础 1.1数据类型

1.分类: -基本类型 -String:任意字符串 -Number:任意的数字 -boolean: true/false -undefined:未定义 -null:空

-引用(对象)类型 -Object:任意对象 -Function:一种特别的对象(可以执行) -Array:一种特别的对象(数值下标,内部数据是有序的)

2.判断: -typeof: 可以判断:undefined、数值、字符串、布尔值 不能判断: null与object object与array instanceof:可以判断对象的具体类型 === 可以判断undefined、null

undefined与null的区别? * undefined表示定义未赋值 * null表示赋值了,值为null 什么时候给变量赋值为null? * 初始赋值,表明将要赋值为对象 * 结束前,让对象成为垃圾对象(被垃圾回收器回收) 3.严格区别变量类型与数据类型? * 数据的类型 - 基本类型 - 对象类型 * 变量的类型(变量内存值的类型) -基本类型:保存就是基本类型的数据 -引用类型:保存的是地址值

1.2数据变量与内存

1.什么是数据? -存储在内存中代表特定的信息,本质上是010101… -数据的特点:可传递、可运算 -内存中所有操作的目标:数据 逻辑运算 算数运算 赋值 运行函数

2.什么是内存? -内存条通电后产生的可储存数据的空间(临时的) -内存产生和死亡:内存条==>通电==>产生内存空间==>存储数据==>处理对象==>断电==>内存空间和数据消失 -一小块内存的2个数据 -内部存储的数据 -地址值

-内存分类 -栈:存储全局变量、局部变量 -堆:存储对象

3.什么是变量? -可变化的的量,又变量名和变量值组成 -每个变量都对应的一小块内存,变量名用来查找对应的内存,变量值就是内存中保存的数据

4.内存,数据,变量三者之间的关系 -内存用来存储数据 -变量是内存的标识

5.var a = xxx; a内存中到底保存的是什么? - xxx 是基本数据,保存的就是这个数据 - xxx 是对象,保存的是对象的地址值 - xxx 是一个变量,保存的xxx的内存内容(可能是基本数据,也可能是地址值)

6.关于引用变量的赋值问题: 2个引用变量指向同一个对象,通过一个变量修改对象内部数据,另一个变量看到的是修改之后的数据。

2个引用变量指向同一个对象,让其中一个引用变量指向另一个对象,另一个引用变量依然指向前一个对象。

var obj1 = {name:"tom"}; var obj2 = obj1; obj2.name = "bob"; console.log(obj1.name); //bob function fun(obj){ obj.name = "TIMI"; }; fun(obj1); console.log(obj2.name); //TIMI var a = {age:12}; var b = a; a = {name:"mike",age:13}; b.age = 14; console.log(b.age,a.name,a.age); //14,mike,13 function fun2(obj){ obj = {age:15}; }; fun2(a); console.log(a.age); //13

7.在js调用函数时传递变量参数时,是值传递还是引用传递? - 理解1:都是值(基本/地址值)传递 - 理解2:可能是值传递,也可能是引用传递(地址值)、

var a = 3; function fun1(a){ a = a+1; }; fun1(a); console.log(a);//3 function fun2(obj){ console.log(obj.name); }; var obj = {name:"tom"}; fun2(obj);

8.js引擎如何管理内存 1.内存生命周期 - 分配小内存空间,得到它的使用权 - 储存数据,可以反复进行操作 - 释放小内存空间

2.释放内存 -局部变量:函数执行完自动释放 对象:成为垃圾对象–>垃圾回收器回收

1.3对象

1.什么是对象? - 多个数据的封装体 - 用来保存多个数据的容器 - 一个对象代表现实中的一个事物

2.为什么用对象? - 统一管理多个数据

3.对象的组成 - 属性:属性名(字符串)和属性值(任意)组成 - 方法:一种特别的属性(属性值是函数)

4.如何访问对象内部的数据? - .属性名: 编码简单,有时不能用 - [“属性名”]: 编码麻烦,能通用

5.什么时候使用 [“属性名”] 这种方式 - 属性名包含特殊字符:- 空格等 - 属性名不确定

var p = {}; // 1.给对象p添加一个属性:content-type:text/json // p.content-type = "text/json"; 不能用 p["content-type"] = "text/json"; console.log(p["content-type"]); // 2.属性名不确定 var propName = "myAge"; var value = 18; // p.propName = value; 不能用 p[propName] = value; console.log(p[propName]); 1.4函数

什么是函数? 实现特定功能的n条语句的封装体 只有函数是可以执行的,其他类型的数据不能执行

如何定义函数? 函数声明 表达式

如何调用函数? test(); 函数名(); 直接调用 obj.test(); 通过对象调用 new test(); new调用 test.call/apply(obj); 临时让test成为obj的方法进行调用

回调函数

什么是回调函数? 1.自己定义的 2.没有调用 3.最终执行了(在某个时刻或某个条件下)

常见的回调函数? 1.dom事件回调函数 2.定时器的回调函数 3.ajax请求回调函数 4.生命周期回调函数

回调 var btn01 = document.getElementById("btn01"); btn01.onclick = function(){ alert(this.innerHTML); }; // 定时器 setTimeout(function(){ alert("到点了"); },2000); 1.5 IIFE

IIFE 全称:Immediately-Invoked Function Expression (立即调用函数表达式),别名:匿名函数自调用。

作用: - 隐藏内部实现 -不会干扰到外部(全局)命名空间 -用它来编码js模块

(function(){ //匿名函数自调用 var a = 3; console.log(a+3); })(); var a = 4; console.log(a); (function(){ var a = 1; function test(){ console.log(++a); } window.$ = function(){ //向外暴露一个全局函数 return{ test:test } }; })(); $().test(); //$是一个函数 //$执行后返回的是一个对象 1.6函数中的this

this是什么? - 在任何函数本质上都是通过某个对象来调用的,如果没有直接指定就是window - 所有函数内部都有一个变量this - 它的值是调用函数的当前对象

如何确定this的值? - test(); window - p.test(); p - new test(); 新创建的对象 - p.call(obj); obj

function Person(color) { console.log(this) this.color = color; this.getColor = function () { console.log(this) return this.color; }; this.setColor = function (color) { console.log(this) this.color = color; }; } Person("red"); //this是谁? window var p = new Person("yello"); //this是谁? p p.getColor(); //this是谁? p var obj = {}; p.setColor.call(obj, "black"); //this是谁? obj var test = p.setColor; test(); //this是谁? window function fun1() { function fun2() { console.log(this); } fun2(); //this是谁? window } fun1(); 二、函数高级 2.1原型与原型链 2.1.1原型(prototype)

1.函数的prototype属性 - 每个函数都有一个prototype属性,它默认指向一个Object空对象(即为原型对象) - 原型对象中有一个属性constructor,它指向函数对象

2.给原型对象添加属性(一般都是方法) - 作用:函数的所有实例对象自动拥有原型中的属性(方法)

// 每个函数都有一个prototype属性,它默认指向一个Object空对象(即为原型对象) console.log(Date.prototype,typeof Date.prototype); function Fun(){ } console.log(Fun.prototype);//默认指向一个Object空对象(没有我们的属性) // 原型对象中有一个属性constructor,它指向函数对象 console.log(Date.prototype.constructor===Date); console.log(Fun.prototype.constructor===Fun); // 给原型对象添加属性(一般是方法)--> 实例对象可以访问 Fun.prototype.test = function(){ console.log('test()'); } var fun = new Fun(); fun.test(); 2.1.2显示原型与隐式原型

1.每个函数function都有一个prototype,即显示原型(属性) 2.每个实例对象都有一个__proto__,可称为隐式原型(属性) 3.对象的隐式原型的值为其对应构造函数的显示原型的值 4.总结: - 函数的prototype属性:在定义函数时自动添加的,默认值是一个空Object对象 - 对象的__proto__属性:创建对象时自动添加的,默认值为构造函数的prototype属性值 - 能直接操作显示原型,但不能直接操作隐式原型(ES6之前)

// 定义构造函数 function Fn(){ // 内部语句:this.prototype = {} } // 1.每个函数function都有一个prototype,即为显示原型属性,默认指向一个空的Object对象 console.log(Fn.prototype); // 2.每个实例对象都有一个__protp__,可称为隐式原型 // 创建实例对象 var fn = new Fn(); // 内部语句:this.__proto__ = Fn.prototype console.log(fn.__proto__); // 3.对象的隐式原型的值为其对应构造函数的显示原型的值 console.log(Fn.prototype === fn.__proto__); //true // 给原型对象添加方法 Fn.prototype.test = function(){ console.log("test()"); } // 通过实例调用原型的方法 fn.test();

显示原型与隐式原型的内存结构图 在这里插入图片描述

2.1.3原型链

原型链(别名:隐式原型链) 访问一个对象属性时, 先在自身属性中查找,找到返回 如果没有,再沿着__proto__这条链向上查找,找到返回 如果最终没有找到,则返回undefined

作用: 1.查找对象的属性(方法) 2.构造函数/原型/实体对象的关系(图解) 3.构造函数/原型/实体对象的关系2(图解)

// console.log(Object); // console.log(Object.prototype); console.log(Object.prototype.__proto__); function Fn(){ this.test1 = function(){ console.log("test1()"); }; } console.log(Fn.prototype); Fn.prototype.test2 = function(){ console.log("test2()"); }; var fn = new Fn(); fn.test1(); fn.test2(); console.log(fn.toString()); console.log(fn.test3); // fn.test3(); /* 1.函数的显示原型指向的对象默认是空Object实例对象(但Object不满足) */ console.log(Fn.prototype instanceof Object); //true console.log(Object.prototype instanceof Object); //false console.log(Function.prototype instanceof Object); //true /* 2.所有函数都是Function的实例(包含Function) */ console.log(Function.__proto__ === Function.prototype); /* Object的原型对象是原型链的尽头 */ console.log(Object.prototype.__proto__); //null

原型链图解 在这里插入图片描述

2.1.4原型链属性问题

1.读取对象的属性值时,会自动到原型链中查找 2.设置对象的属性值时,不会查找原型链,如果当前对象中没有此属性,直接添加此属性并设置其值 3.方法一般定义在原型中,属性一般通过构造函数定义在对象本身上

function Fn(){ } Fn.prototype.a = "xxx"; var fn1 = new Fn(); console.log(fn1.a,fn1); var fn2 = new Fn(); fn2.a = "yyy"; console.log(fn1.a,fn2.a,fn2); function Person(name,age){ this.name = name; this.age = age; } Person.prototype.setName = function(name){ this.name = name; }; var p1 = new Person("Tom",12); p1.setName("libai"); var p2 = new Person("jack",12); p2.setName("nice"); console.log(p1); console.log(p2); console.log(p1.__proto__ === p2.__proto__); //true 2.1.5探索instanceof

1.instanceof是如何判断的? 表达式:A instanceof B 如果B函数的显示原型对象在A对象的原型链上,返回true,否则返回false

2.Function是通过new自己产生的实例

/* 案例1 */ function Foo(){} var f1 = new Foo(); console.log(f1 instanceof Foo); //true console.log(f1 instanceof Object); //true /* 案例2 */ console.log(Object instanceof Function); //true console.log(Object instanceof Object); //true console.log(Function instanceof Function); //true console.log(Function instanceof Object); //true function Foo(){}; console.log(Object instanceof Foo); //false 2.1.6原型面试题 /* 测试题1*/ function A(){} A.prototype.n = 1; var b = new A(); A.prototype = { n:2, m:3 }; var c = new A(); console.log(b.n,b.m,c.n,c.m); //1 undefined 2 3 /* 测试题2 */ function F(){} Object.prototype.a = function(){ console.log("a()"); }; Function.prototype.b = function(){ console.log("b()"); }; var f = new F(); f.a(); // f.b(); 不能执行 F.a(); F.b(); console.log(f); console.log(Object.prototype); console.log(Function.prototype);

测试一图解 在这里插入图片描述

2.2执行上下文与执行上下文栈 2.2.1变量提升与函数提升

1.变量的声明提升

通过var定义(声明)的变量,在定义语句之前就可以访问到,值为:undefined

2.函数声明提升

通过function声明的函数,在之前就可以直接调用,值为:函数定义(对象)

3.问题:变量提升和函数提升是如何产生的?

/* 面试题 */ // 输出a的值 var a = 3; function fn(){ console.log(a); var a = 4; } fn(); //a=undefined console.log(b); //undefined 变量提升 fn2(); //可调用 函数提升 // fn3(); 不能调用,变量提升 var b = 3; function fn2(){ console.log("fn2()"); } var fn3 = function(){ console.log("fn3()"); }; 2.2.2执行上下文

1.代码分类(位置) - 全局代码 - 函数(局部)代码

2.全局执行上下文 - 在执行全局代码前将window确定为全局执行上下文 - 对全局数据进行预处理 * var 定义的全局变量–>undefined,添加为window的属性 * function声明的全局函数–>赋值(fun),添加为window的方法 * this–>赋值(window) - 开始执行全局代码

3.函数执行上下文 - 在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象(虚拟的,存在于栈中) - 对局部数据进行预处理 * 形参变量–>赋值(实参)–>添加为执行上下文的属性 * arguments–>赋值(实参列表),添加为执行上下文属性 * var 定义的局部变量–>undefined,添加为执行上下文的属性 * function声明的函数–>赋值(fun),添加为执行上下文的方法 * this–>赋值(调用函数的对象) - 开始执行函数体代码

/* 全局执行上下文 */ console.log(a1,window.a1); //undefined,undefined a2(); //a2() console.log(this); //window var a1 = 3; function a2(){ console.log("a2()"); } console.log(a1); //3 console.log("========="); /* 函数执行上下文 */ function fn(a1){ console.log(a1); console.log(a2); a3(); console.log(this); console.log(arguments); //伪数组(2,3) var a2 = 3; function a3(){ console.log("a3()"); } } fn(2,3); //2,undefined,a3(),window 2.2.3执行上下文栈

1.在全局代码执行前,js引擎就会创建一个栈来存储管理所有的执行上下文对象 2.在全局执行上下文(window)确定后,将其添加到栈中(压栈) 3.在函数执行上下文创建后,将其添加到栈中(压栈) 4.在当前函数执行完后,将栈顶的对象移除(出栈) 5.当所有的代码执行完之后,栈中只剩下window

var a = 10; var bar = function(x){ var b = 5; foo(x+b); }; var foo = function(y){ var c = 5; console.log(a+c+y); }; bar(10); //30 2.2.4执行上下文2 /* 1.以下代码依次输出什么? gb:undefined fb:1 fb:2 fb:3 fe:3 fe:2 fe:1 ge:1 2.整个过程中产生了几次执行上下文? 5次 */ console.log("gb:"+i); var i = 1; foo(1); function foo(i){ if(i==4){ return; } console.log("fb:"+i); foo(i + 1); //递归调用 console.log("fe:"+i); } console.log("ge:"+i); 2.2.5面试题 /* 测试题1 先执行变量提升,再执行函数提升 */ function a(){} var a; console.log(typeof a); //function /* 测试题2 */ if(!(b in window)){ var b = 1; } console.log(b); //undefined /* 测试题3 */ var c = 1; function c(c){ console.log(c); var c = 3; } c(2); //报错,c不是函数 2.3作用域与作用域链 2.3.1作用域

作用域 1.理解: - 指一块空间,代码所在的区域 - 它是静态的(相对于上下文对象),在编写代码时就确定了

2.分类: - 全局作用域 - 函数作用域 - 没有块作用域(ES6开始有了)

3.作用: - 隔离变量,不同作用域下同名变量不会有冲突

// 没有块作用域 /* if(true){ var c = 3; } console.log(c); */ var a = 10; var b = 20; function fn(x){ var a = 100; c = 300; console.log("fn()",a,b,c,x); function bar(x){ var a = 1000; var d = 400; console.log("bar()",a,b,c,d,x); } bar(100); bar(200); } fn(10); 2.3.2作用域与执行上下文

作用域与执行上下文的区别: 区别1 - 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了,而不是在函数调用时, - 全局执行上下文环境是在全局作用域确定之后,js代码马上执行之前创建 - 函数执行上下文是在调用函数时,函数体代码执行之前创建

区别2 - 作用域是静态的,只要函数定义好了就一直存在,且不会再变化 - 执行上下文是动态的,调用函数时创建,函数调用结束时就会自动释放

联系 - 上下文环境(对象)是从属于所在的作用域 - 全局上下文环境–>全局作用域 - 函数上下文环境–>对应的函数作用域

var a = 10; var b = 20; function fn(x){ var a = 100; c = 300; console.log("fn()",a,b,c,x); function bar(x){ var a = 1000; var d = 400; console.log("bar()",a,b,c,d,x); } bar(100); bar(200); } fn(10); 2.3.3作用域链

1.理解 - 多个上下级关系的作用域形成的链,它的方向是从下向上的(从内到外) - 查找变量时就是沿着作用域链来查找的

2.查找一个变量的查找规则 - 在当前作用域下的执行上下文中查找对应的属性,如果有直接返回,否则进入2 - 在上一级作用域的执行上下文中查找对应的属性,如果有直接返回,否则进入3 - 再次执行2的相同操作,知道全局作用域,如果还找不到就抛出找不到的异常

/* 作用域链 */ var a = 1; function fn1(){ var b = 2; function fn2(){ var c = 3; console.log(c); console.log(b); console.log(a); console.log(d); //d未定义,会报错 } fn2(); } fn1(); 2.3.4作用域面试题 var x = 10; function fn(){ console.log(x); } function show(f){ var x = 20; f(); } show(fn); //10 // fn的作用域没有x,只能从外部的作用域去找 console.log("========="); var fn = function(){ console.log(fn); }; fn(); var obj = { fn2:function(){ console.log(fn2); //会报错,fn2未被定义 // console.log(this.fn2); 想要输出该作用域的fn2,须在前面加this. } }; obj.fn2(); 2.4闭包 2.4.1理解闭包

1.如何产生闭包? 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包。

2.闭包到底是什么? 理解一:闭包是嵌套的内部函数 理解二:包含被引用变量(函数)的对象 注:闭包存在于嵌套的内部函数中

3.产生闭包的条件? 函数嵌套 内部函数引用了外部函数的数据(变量/函数)

function fn1(){ var a = 2; var b = "abc"; function fn2(){ //执行函数定义就会产生闭包(不用调用内部函数) console.log(a); } fn2(); } fn1(); 2.4.2常见的闭包

1.将函数作为另一个函数的返回值 2.将函数作为实参传递给另一个函数调用

// 1.将函数作为另一个函数的返回值 function fn1(){ var a = 2; function fn2(){ a++; console.log(a); } return fn2; } var f = fn1(); f(); //3 f(); //4 // 2.将函数作为实参传递给另一个函数调用 function showDelay(msg,time){ setTimeout(function(){ alert(msg); },time); } showDelay("学 习",2000); 2.4.3闭包的作用

闭包的作用 1.使用函数内部的变量在函数执行完后,仍然存活在内存中(延长了局部变量的生命周期) 2.让函数外部可以操作(读写)到函数内部的数据(变量/函数)

问题: 1.函数执行完后,函数内部声明的局部变量是否还存在? 一般是不存在的,存在于闭包的变量才可能存在

2.在函数外部能直接访问函数内部的局部变量么? 不能,但我们可以通过闭包让外部操作它

function fn1(){ var a = 2; function fn2(){ a++; console.log(a); } function fn3(){ a--; console.log(a); } return fn3; } var f = fn1(); f(); //1 f(); //0 2.4.4闭包的生命周期

1.产生:在嵌套内部函数定义执行完时就产生了(不是在调用) 2.死亡:在嵌套的内部函数成为垃圾对象时

function fn1(){ // 此时闭包就已经产生了(函数提升,内部函数对象已经创建了) var a = 2; function fn2(){ a++; console.log(a); } return fn2; } var f = fn1(); f(); //3 f(); //4 f = null; //闭包死亡(包含闭包的函数对象成为垃圾对象) 2.4.5闭包的应用_自定义JS模块

闭包的应用:自定义JS模块 - 具有特定功能的js文件 - 将所有的数据和功能都封装在一个函数内部(私有的) - 只向外暴露一个包,执行n个方法的对象或函数 - 模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能

myModule2.doSomething(); myModule2.doOtherthing();

myModule2.js

(function(){ // 私有数据 var msg = "My Class"; // 操作数据的函数 function doSomething(){ console.log("doSomething()"+msg.toUpperCase()); } function doOtherthing(){ console.log("doOtherthing()"+msg.toLowerCase()); } // 向外暴露对象(给外部使用的方法) window.myModule2 = { doSomething:doSomething, doOtherthing:doOtherthing } })() 2.4.6闭包的缺点及解决

1.缺点: - 函数执行完后,函数内的局部变量没有释放,占用内存时间会变长 - 容易造成内存泄漏

2.解决 - 能不用闭包就不用 - 及时释放

function fn1(){ var arr = new Array[100000]; function fn2(){ console.log(arr.length); } return fn2; } var f = fn1(); f(); f = null; //让内部函数成为垃圾对象-->回收闭包 2.4.7内存溢出与内存泄漏

1.内存溢出 - 一种程序运行出现的错误 - 当程序运行需要的内存超过了剩余内存时,就会抛出内存溢出的错误

2.内存泄漏 - 占用的内存没有及视释放 - 内存泄漏积累的多了就容易导致内存溢出

常见的内存泄漏: * 意外的全局变量 * 没有及时清理的计时器或回调函数 * 闭包

// 内存溢出 var obj = {}; for(var i = 0;i a = new Array(1000000); console.log(a); } fn(); // 没有及时清理的计时器或回调函数 var start = setInterval(function(){ //启动定时器后不清理 console.log("---"); },2000); // 清理定时器 clearInterval(start); // 闭包 function fn1(){ var a = 4; function fn2(){ console.log(++a); } return fn2; } var f = fn1(); f(); // f = null; 2.4.8闭包面试题 /* 以下代码输出的是什么 */ // 代码片段一 var name = "The Window"; var object = { name:"My Object", getNameFunc:function(){ return function(){ return this.name; }; } }; alert(object.getNameFunc()()); // the window // 函数嵌套,但是内部函数没有调用外部函数的变量,不是闭包 // 代码片段二 var name2 = "The Window"; var object2 = { name2:"My Object", getNameFunc:function(){ var that = this; return function(){ return that.name2; }; } }; alert(object2.getNameFunc()()); //My Object // 函数嵌套,内部函数调用了外部函数的that,是闭包 function fun(n,o){ console.log(o); return{ fun:function(m){ return fun(m,n); } } } var a = fun(0); a.fun(1); a.fun(2); a.fun(3); // undefined,0,0,0 var b = fun(0).fun(1).fun(2).fun(3); //undefined,0,1,2 var c = fun(0).fun(1); c.fun(2); c.fun(3); //undefined,0,1,1 三、对象高级 3.1对象的创建模式 3.1.1Object构造函数模式

方式一:Object构造函数模式 - 套路:先创建空Object对象,再动态添加属性/方法 - 使用场景:起始时不确定对象内部数据 - 问题:代码量多

var p = new Object(); p.name = "tom"; p.age = 12; p.setName = function(name){ this.name = name; }; // 测试 p.setName("李白"); console.log(p.name,p.age); 3.1.2对象字面量模式

方式二:对象字面量模式 - 套路:使用{}创建对象,同时指定属性和方法 - 适用场景:起始时对象内部数据时确定的 - 问题:如果创建多个对象,有重复代码

var p = { name:"tom", age:16, setName:function(name){ this.name = name; } }; // 测试 console.log(p.name,p.age); p.setName("Jack"); console.log(p.name,p.age); 3.1.3工厂模式

方式三: 工厂模式 - 套路:通过工厂函数动态创建对象并返回 - 适用场景:需要创建多个对象 - 问题:对象没有一个具体的类型,都是Object类型

function createPerson(age,name){ //返回一个对象的函数-->工厂函数 var obj = { age:age, name:name, setName:function(){ this.name = name; } }; return obj; } var p1 = createPerson(16,"张三"); var p2 = createPerson(16,"李三"); console.log(p1); console.log(p2); function createCat(age,name){ var obj = { age:age, name:name, setName:function(){ this.name = name; } }; return obj; } var c1 = createCat(15,"波斯猫") var c2 = createCat(15,"野猫") console.log(c1); console.log(c2); 3.1.4自定义构造函数模式

方式四:自定义构造函数模式 套路:自定义构造函数,通过new创建对象 适用场景:需要创建多个类型确定的对象 问题:每个对象都有相同的数据,浪费内存

// 定义类型 function Person(name,age){ this.name = name; this.age = age; this.setName = function(){ this.name = name; }; } var p1 = new Person("lisa",16); p1.setName("bob"); console.log(p1.name,p1.age); console.log(p1 instanceof Person); function Student(name,price){ this.name = name; this.price = price; this.setName = function(){ this.name = name; }; } var s = new Student("熊大",1000); console.log(s instanceof Student); var p2 = new Person("Jack",16); console.log(p1,p2) 3.1.5构造函数+原型组合模式

方式五:构造函数+原型组合 套路:自定义构造函数,属性在函数中初始化,方法添加到原型上 适用场景:需要创建多个类型确定的对象

function Person(name,age){ this.name = name; this.age = age; } Person.prototype.setName = function(name){ this.name = name; }; var p1 = new Person("tom",16); var p2 = new Person("bob",19); console.log(p1,p2); 3.2继承模式 3.2.1原型链继承

原型链继承:方式一 套路: 1.定义父类型的构造函数 2.给父类型的原型添加方法 3.定义子类型的构造函数 4.创建父类型的对象赋值给子类型的原型 5.将子类型原型的构造属性设置为子类型 6.给子类型原型添加方法 7.创建子类型的对象:可以调用父类型的方法

关键 - 子类型的原型为父类型的一个实例对象

// 父类型 function Supper(){ this.supProp = "Supper property"; } Supper.prototype.showSupperProp = function(){ console.log(this.supProp); } // 子类型 function Sub(){ this.subProp = "Sub property"; } // 子类型的原型为父类型的一个实例对象 Sub.prototype = new Supper(); // 让子类型的原型的constructor指向子类型 Sub.prototype.constructor = Sub; Sub.prototype.showSubProp = function(){ console.log(this.subProp); } var sub = new Sub(); sub.showSupperProp(); sub.showSubProp(); console.log(sub); //Sub

原型链继承结构图: 在这里插入图片描述

3.2.2借用构造函数继承

方式二:借用构造函数继承(假的) 1.套路: - 定义父类型的构造函数 - 定义子类型的构造函数 - 在子类型构造函数中调用父类型构造

2.关键: - 在子类型构造函数中通用call()调用父类型构造函数

function Person(name,age){ this.name = name; this.age = age; } function Student(name,age,price){ Person.call(this,name,age); //相当于:this.Person(name,age) /* this.name = name; this.age = age; */ this.price = price; } var s = new Student("tom",20,14000); console.log(s.name,s.age,s.price); 3.2.3组合继承

方式三:原型链+借用构造函数的组合继承 1.利用原型链实现对父类型对象的方法继承 2.利用call借用父类型构造函数初始化相同属性

function Person(name,age){ this.name = name; this.age = age; } Person.prototype.setName = function(name){ this.name = name; }; function Student(name,age,price){ Person.call(this,name,age); //为了得到属性 this.price = price; } Student.prototype = new Person(); //为了能看到父类型的方法 Student.prototype.constructor = Student; //修正constructor属性 Student.prototype.setPrice = function(price ){ this.price = price; }; var s = new Student("tom",24,15000); s.setName("bob"); s.setPrice(16000); console.log(s.name,s.age,s.price); 四、线程机制与事件机制 4.1进程与线程

进程:程序的一次执行,它占有一片独有的内存空间,可以通过windows任务管理器查看进程 - 多进程运行:一应用程序可以同时启动多个实例运行

线程:是进程内的一个独立执行单元,试程序执行的一个完整流程,是CPU的最小调度单元

多线程:在一个进程内,同时有多个线程运行 优点: 1.有效提升CPU的利用率 缺点: 1.创建多线程开销 2.线程间切换开销 3.死锁与状态同步问题

单线程: 优点:顺序编程简单易懂 缺点:效率低

相关知识: - 应用程序必须运行在某个进程的某个路线上 - 一个进程中至少有一个运行的线程:主线程,进程启动后自动创建 - 一个进程中也可以同时运行多个线程,我们会说程序是多线程运行的 - 一个进程内的数据可以供其中的多个线程直接共享 - 多个进程之间的数据是不能直接共享的 - 线程池:保存多个线程对象的容器,实现线程对象的反复利用

4.2定时器引发的思考

1.定时器真的是定时执行的么? - 定时器并不能保证真正定时执行 - 一般会延迟一点,也有可能延迟很长时间

2.定时器回调函数是在分线程执行的吗? - 在主线程执行的,js是单线程的

3.定时器是如何实现的? 事件循环模型

启动定时器 document.getElementById("btn").onclick = function(){ var start = Date.now(); console.log("启动定时器前"); setTimeout(function(){ console.log("定时器执行了",Date.now()-start); },200); } console.log("定时器启动后"); // 做一个长时间的工作 for(var i = 0;i console.log("fn1()"); } fn1(); document.getElementById("btn").onclick = function(){ console.log("点击了btn"); }; setTimeout(function(){ console.log("定时器执行了"); },2000); function fn2(){ console.log("fn2()"); } fn2(); 4.5Web Workers

1.h5规范提供了js分线程的实现,取名为:Web Workers 2.相关API - Worker:构造函数,加载分线程执行的js文件 - Worker.prototype.onmessage: 用于接收另一个线程的回调函数 - Worker.prototype.postMessage: 向另一个线程发送消息

3.不足: - worker内代码不能操作DOM - 不能跨域加载js - 不是每个浏览器都支持这个新特性

计算 var input = document.getElementById("number"); document.getElementById("btn").onclick = function(){ var number = input.value; // 创建一个Worker对象 var worker = new Worker("worker.js"); // 绑定接收消息的监听 worker.onmessage = function(event){ console.log("主线程接收分线程返回"); alert(event.data); }; // 向分线程发送信息 worker.postMessage(number); console.log("主线程向分线程发送数据"+number); };

worker.js

function fibonacci(n){ return n


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3